package com.lqian.demo02.part2.transaction;

import com.lqian.demo02.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 事务概念：
 *事务指逻辑上的一组操作，组成这组操作的各个单元，要不全部成功，要不全部不成功。
 *——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
 * ACID 原则：
 * 原子性(Atomic)
 * 整个事务中的所有操作，要么全部完成，要么全部不完成，不可能停滞在中间某个环节。事务在执
 * 行过程中发生错误，会被回滚（ROLLBACK）到事务开始前的状态，就像这个事务从来没有执行过
 * import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; public class SQL注入 { public static void main(String[] args) { // login("zhangsan","123456"); // 正常登陆 login("'or '1=1","123456"); // SQL 注入 }public static void login(String username,String password){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try{conn = JdbcUtils.getConnection(); // select * from users where name='' or '1=1' and password = '123456' String sql = "select * from users where name=? and password=?"; st = conn.prepareStatement(sql); st.setString(1,username); st.setString(2,password); rs = st.executeQuery(); while(rs.next()){ System.out.println(rs.getString("name")); System.out.println(rs.getString("password")); System.out.println("=============="); } }catch (Exception e) { e.printStackTrace(); }finally{ JdbcUtils.release(conn, st, rs); } } } 4567891011121314151617181920212223242526272829303132333435363738394041
 * 一样。
 * 一致性(Consist)
 * 一个事务可以封装状态改变（除非它是一个只读的）。事务必须始终保持系统处于一致的状态，不
 * 管在任何给定的时间并发事务有多少。也就是说：如果事务是并发多个，系统也必须如同串行事务
 * 一样操作。其主要特征是保护性和不变性(Preserving an Invariant)，以转账案例为例，假设有五
 * 个账户，每个账户余额是100元，那么五个账户总额是500元，如果在这个5个账户之间同时发生多
 * 个转账，无论并发多少个，比如在A与B账户之间转账5元，在C与D账户之间转账10元，在B与E之
 * 间转账15元，五个账户总额也应该还是500元，这就是保护性和不变性。
 * 隔离性(Isolated)
 * 隔离状态执行事务，使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务，运行在相
 * 同的时间内，执行相同的功能，事务的隔离性将确保每一事务在系统中认为只有该事务在使用系
 * 统。这种属性有时称为串行化，为了防止事务操作间的混淆，必须串行化或序列化请求，使得在同
 * 一时间仅有一个请求用于同一数据。
 * 持久性(Durable)
 * 在事务完成以后，该事务对数据库所作的更改便持久的保存在数据库之中，并不会被回滚。
 *——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
 * 隔离性问题：
 * 1、脏读：脏读指一个事务读取了另外一个事务未提交的数据。
 * 2、不可重复读：不可重复读指在一个事务内读取表中的某一行数据，多次读取结果不同。
 * 3、虚读(幻读) : 虚读(幻读)是指在一个事务内读取到了别的事务插入的数据，导致前后读取不一致。
 *——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
 *当Jdbc程序向数据库获得一个Connection对象时，默认情况下这个Connection对象会自动向数据库提交
 * 在它上面发送的SQL语句。若想关闭这种默认提交方式，让多条SQL在一个事务中执行，可使用下列的
 * JDBC控制事务语句:
 * Connection.setAutoCommit(false);//开启事务(start transaction)
 * Connection.rollback();//回滚事务(rollback)
 * Connection.commit();//提交事务(commit)
 * ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
 *
 *
 * 模拟转账成功时的业务场景
 * @author：Melody_LQ
 * @time：2022/7/14 11:24 上午
 */
public class Transcation01 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JDBCUtils.getConnection();
            conn.setAutoCommit(false);

            String sql1 = "update account set money=money-100 where name='A'";
            st = conn.prepareStatement(sql1);
            st.executeUpdate();
            String sql2 = "update account set money=money+100 where name='B'";
            st = conn.prepareStatement(sql2);
            st.executeUpdate();

            conn.commit();
            //上面的两条SQL执行Update语句成功之后就通知数据库提交事务 (commit)
            System.out.println("成功！！！"); //log4j
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.release(conn,st,rs);
        }
    }
}
